package com.kaoshi.jeeweb.common.web.argument.resolver;

import com.google.common.collect.Lists;
import com.kaoshi.jeeweb.common.web.argument.annotation.SearchConditionDefaults;
import com.kaoshi.jeeweb.common.web.argument.search.SearchCondition;
import com.kaoshi.myweb.common.seachable.Criteria;
import com.kaoshi.myweb.common.seachable.QueryCondition;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.MethodParameter;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * Created by ZHOUTIEQIANG053 on 2016-12-09.
 */
public class SearchConditionResolver extends BaseMethodArgumentResolver {

	private static final String DEFAULT_SEARCH_PREFIX = "search";
	private static final String DEFAULT_PAGE_PREFIX = "page";
	private static final String DEFAULT_SORT_PREFIX = "orderBy";

	//前端传递页码的名称
	private static final String PAGE_NO_PARAM_NAME = "pageNo";

	//前端传递每页显示条数的名称
	private static final String PAEG_SIZE_PARAM_NAME = "pageSize";

	private String searchPrefix = DEFAULT_SEARCH_PREFIX;
	private String pagePrefix = DEFAULT_PAGE_PREFIX;
	private String sortPrefix = DEFAULT_SORT_PREFIX;


	private int minPageSize = 5;
	private int maxPageSize = 100;


	/**
	 * 设置查询参数前缀，可在配置中改变
	 *
	 * @param prefix
	 */
	public void setsearchPrefix(String prefix) {
		this.searchPrefix = prefix;
	}

	public void setPagePrefix(String pagePrefix) {
		this.pagePrefix = pagePrefix;
	}

	public void setSortPrefix(String sortPrefix) {
		this.sortPrefix = sortPrefix;
	}


	@Override
	public boolean supportsParameter(MethodParameter methodParameter) {
		return SearchCondition.class.isAssignableFrom(methodParameter.getParameterType());
	}

	@Override
	public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {

		String searchPrefix = this.searchPrefix;

		Map<String, String[]> searcheableMap = getPrefixParameterMap(searchPrefix, webRequest, true);

		boolean hasCustomSearchFilter = searcheableMap.size() > 0;

		//默认查询条件，如果，页面未传递值，则使用默认
		SearchConditionDefaults searchDefaults = getSearchableDefaults(methodParameter);

		boolean needMergeDefault = searchDefaults != null && searchDefaults.merge();

		QueryCondition queryCondition = null;
		//自定义覆盖默认
		if (needMergeDefault || !hasCustomSearchFilter) {
			queryCondition = getDefaultFromAnnotation(searchDefaults);
		}
		if (hasCustomSearchFilter) {
			if (queryCondition == null) {
				queryCondition = new QueryCondition();
				queryCondition.or(Criteria.newCriteria());
			}
			int index = 0;
			for (String name : searcheableMap.keySet()) {
				index = name.lastIndexOf("_");

				if(index > 0){
					//支持search.del_flag_eq#date格式
					String paramName = name.substring(0, index);
					String paramCondition = name.substring(index + 1);;
					String[] mapValues = filterSearchValues(searcheableMap.get(name));
					Criteria criteria = queryCondition.getCriterias().get(0);

					getCriteria(criteria, paramName, paramCondition,  mapValues[0]);
				}

			}
		}




		Map<String, String[]> pageableMap = getPrefixParameterMap(pagePrefix, webRequest, false);
		Map<String, String[]> sortMap = getPrefixParameterMap(sortPrefix, webRequest, false);

		//needPage=false needSort=true  不要分页，但排序
		if (searchDefaults != null && !searchDefaults.needPage() && searchDefaults.needSort()) {
			setSort(queryCondition, sortPrefix, sortMap, searchDefaults, webRequest);
		}

		int pn = getPn(pageableMap, searchDefaults);
		int pageSize = getPageSize(pageableMap, searchDefaults);


		SearchCondition searchCondition = new SearchCondition();
		searchCondition.setNeedPage(searchDefaults.needPage());
		searchCondition.setNeedSort(searchCondition.isNeedSort());
		searchCondition.setPageNumber(pn);
		searchCondition.setPageSize(pageSize);
		searchCondition.setQueryCondition(queryCondition);

		return searchCondition;
	}

	private void setSort(QueryCondition queryCondition, String sortNamePrefix, Map<String, String[]> sortMap, SearchConditionDefaults searchConditionDefaults, NativeWebRequest webRequest) {

		if(sortMap.size() == 0){
			Map<String, String[]> defaultMap = new LinkedHashMap<String, String[]>();

			//{"a=desc, a.b=desc"}
			String[] sorts = searchConditionDefaults.sort();
			for (int j = 0; j < sorts.length; j++){
				String[] arrs = sorts[j].split("=");
				defaultMap.put(arrs[0], new  String[]{arrs[1]});
			}

			sortMap = defaultMap;
		}

		for (String name : sortMap.keySet()) {
			String orderStr = name;
			String orderDirction = sortMap.get(name)[0];

			if(orderDirction.toLowerCase(Locale.ENGLISH).equals("desc")){
				queryCondition.desc(name);
			}else{
				queryCondition.asc(name);
			}
		}

	}

	/**
	 * 防止sql注入，排序字符串只能包含字符 数字 下划线 点 ` "
	 *
	 * @param property
	 */
	private void assertSortProperty(String property) {
		if (!property.matches("[a-zA-Z0-9_、.`\"]*")) {
			throw new IllegalStateException("Sort property error, only contains [a-zA-Z0-9_.`\"]");
		}
	}

	private int getPageSize(Map<String, String[]> pageableMap, SearchConditionDefaults searchConditionDefaults) {
		int pageSize = 0;
		try {
			String pageSizeStr = pageableMap.get(PAEG_SIZE_PARAM_NAME)[0];
			if (pageSizeStr != null) {
				pageSize = Integer.valueOf(pageSizeStr);
			} else {
				pageSize = searchConditionDefaults.pageSize();
			}
		} catch (Exception e) {
			pageSize = searchConditionDefaults.pageSize();
		}

		if (pageSize < minPageSize) {
			pageSize = minPageSize;
		}

		if (pageSize > maxPageSize) {
			pageSize = maxPageSize;
		}
		return pageSize;
	}

	private int getPn(Map<String, String[]> pageableMap, SearchConditionDefaults searchConditionDefaults) {
		int pn = 1;
		try {
			String pnStr = pageableMap.get(PAGE_NO_PARAM_NAME)[0];
			if (pnStr != null) {
				pn = Integer.valueOf(pnStr);
			} else {
				pn = searchConditionDefaults.pageNumber();
			}
		} catch (Exception e) {
			pn = searchConditionDefaults.pageNumber();
		}

		if (pn < 1) {
			pn = 1;
		}

		return pn;
	}

	private String[] filterSearchValues(String[] values) {
		List<String> result = Lists.newArrayList(CollectionUtils.arrayToList(values));
		for (int i = 0; i < result.size(); i++) {
			if (StringUtils.isBlank(result.get(i))) {
				result.remove(i);
			}
		}
		return result.toArray(values);
	}

	private String getSearchPrefix(MethodParameter parameter) {
		Qualifier qualifier = parameter.getParameterAnnotation(Qualifier.class);

		if (qualifier != null) {
			return new StringBuilder(((Qualifier) qualifier).value()).append("_").append(searchPrefix).toString();
		}

		return searchPrefix;
	}


	private SearchConditionDefaults getSearchableDefaults(MethodParameter parameter) {
		//首先从参数上找
		SearchConditionDefaults searchDefaults = parameter.getParameterAnnotation(SearchConditionDefaults.class);
		//找不到从方法上找
		if (searchDefaults == null) {
			searchDefaults = parameter.getMethodAnnotation(SearchConditionDefaults.class);
		}
		return searchDefaults;
	}


	private QueryCondition getDefaultFromAnnotation(SearchConditionDefaults searchableDefaults) {

		QueryCondition queryCondition = defaultSearchable(searchableDefaults);
		if (queryCondition != null) {
			return queryCondition;
		}

		queryCondition = new QueryCondition();
		queryCondition.or(Criteria.newCriteria());
		return queryCondition;
	}

	private QueryCondition defaultSearchable(SearchConditionDefaults searchableDefaults) {

		if (searchableDefaults == null) {
			return null;
		}

		QueryCondition searchable = new QueryCondition();

		Criteria criteria = Criteria.newCriteria();
		searchable.or(criteria);

		for (String searchParam : searchableDefaults.value()) {
			String[] searchPair = searchParam.split("=");
			String[] searchCondition = searchPair[0].split("_");
			String paramName = searchCondition[0];
			String paramCondition = searchCondition[1];
			String paramValue = searchPair[1];

			getCriteria(criteria, paramName, paramCondition, paramValue);
		}

		return searchable;
	}

	private void getCriteria(Criteria criteria, String paramName, String paramCondition, String paramValue) {
		String condition = paramCondition.toLowerCase(Locale.ENGLISH);

		if(StringUtils.isBlank(paramValue)){
			return;
		}

		//eq#date,时间类型字段，先转换格式，再查询
		if(condition.contains("#")){
			String[] conditions = condition.split("#");
			condition = conditions[0];

			String conditin2 = conditions[1];

			if(conditin2.equals("date")){
				paramName = "date_format(" + paramName + ", '%Y-%m-%d')";
			}else if(conditin2.equals("time")){
				paramName = "date_format(" + paramName + ", '%H-%i-%s')";
			}else if(conditin2.equals("datetime")) {
				paramName = "date_format(" + paramName + ", '%Y-%m-%d %H-%i-%s')";
			}
		}

		if (condition.equals("in")) {
			List<String> list = Lists.newArrayList( CollectionUtils.arrayToList(paramValue.split(",")));
			list.add(paramValue);
			criteria.in(paramName, list);
		} else if (condition.equals("notin")) {
			List<String> list = Lists.newArrayList( CollectionUtils.arrayToList(paramValue.split(",")));
			list.add(paramValue);
			criteria.notIn(paramName, list);
		} else if (condition.equals("eq")) {
			criteria.eq(paramName, paramValue);
		} else if (condition.equals("noteq")) {
			criteria.notEq(paramName, paramValue);
		} else if (condition.equals("gt")) {
			criteria.gt(paramName, paramValue);
		} else if (condition.equals("ge")) {
			criteria.ge(paramName, paramValue);
		} else if (condition.equals("lt")) {
			criteria.lt(paramName, paramValue);
		} else if (condition.equals("le")) {
			criteria.le(paramName, paramValue);
		} else if (condition.equals("like")) {
			criteria.like(paramName, "%" + paramValue + "%");
		}else if (condition.equals("llike")) {
			criteria.like(paramName, "%" + paramValue );
		}else if (condition.equals("rlike")) {
			criteria.like(paramName, paramValue + "%");
		}else if (condition.equals("notlike")) {
			criteria.notLike(paramName, "%" +paramValue + "%");
		}else if (condition.equals("notllike")) {
			criteria.notLike(paramName, "%" +paramValue);
		}else if (condition.equals("notrlike")) {
			criteria.notLike(paramName, paramValue + "%");
		}
	}


}
